﻿//-----------------------------------------------------------------------
// <copyright file="WebflowContext.cs" company="Sławomir Rosiek">
//     Copyright (c) Sławomir Rosiek. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------

namespace Rosieks.Web.Flow
{
    using System;
    using System.Collections.Generic;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Routing;
    using Rosieks.Web.Flow.Internals;
    using Rosieks.Web.Flow.Metadata;

    [Serializable]
    public class WebflowContext
    {
        public WebflowContext()
        {
            this.History = new Stack<string>();
        }

        internal const string WebflowContextKey = "__CurrentWebflow__";

        #region Fields

        [NonSerialized]
        private WebflowModel webflowModel;

        [NonSerialized]
        private State currentState;

        [NonSerialized]
        private IExecutionObject currentElement;

        #endregion

        #region Properties

        public WebflowModel Webflow
        {
            get { return this.webflowModel; }
            set { this.webflowModel = value; }
        }

        public State CurrentState
        {
            get
            {
                return this.currentState;
            }

            set
            {
                this.currentState = value;
                this.CurrentStateName = value.Maybe(v => v.Name);
            }
        }

        public IExecutionObject CurrentElement
        {
            get { return this.currentElement ?? this.currentState; }
            set { this.currentElement = value; }
        }

        public Stack<string> History { get; set; }

        public object Model
        {
            get;
            set;
        }

        internal string CurrentStateName
        {
            get;
            set;
        }

        #endregion

        #region Current WebflowContext access methods

        public static WebflowContext GetCurrent(HttpContextBase httpContext)
        {
            return httpContext.Items[WebflowContext.WebflowContextKey] as WebflowContext;
        }

        public static void SetCurrent(HttpContextBase httpContext, WebflowContext context)
        {
            httpContext.Items[WebflowContext.WebflowContextKey] = context;
        }

        #endregion

        /// <summary>
        /// Called by the Webflow library before flow start.
        /// </summary>
        public void OnStart()
        {
            this.CurrentState = this.Webflow.FirstState;
            this.History.Clear();
            this.History.Push(this.CurrentState.Name);

            this.Webflow.OnStart(this);
        }

        /// <summary>
        /// Called by the Webflow library after flow finish.
        /// </summary>
        public void OnFinish()
        {
            this.Webflow.OnFinish(this);
        }

        public void OnStateEnter(ControllerContext context)
        {
            this.CurrentState.OnEnter(this, context);
        }

        public void OnStateLeave(ControllerContext context)
        {
            this.CurrentState.OnLeave(this, context);
        }

        /// <summary>
        /// Called by the Webflow library after state loading.
        /// </summary>
        public void OnLoad()
        {
            this.CurrentElement = null;

            this.CurrentState = this.Webflow[this.CurrentStateName];
        }

        public void OnSaving()
        {

        }

        /// <summary>
        /// Called by the Webflow library after action method executes.
        /// </summary>
        /// <param name="actionName">Name of the action to execute</param>
        public void OnAction(string actionName, ControllerContext context)
        {
            this.CurrentState.OnAction(this, actionName, context);
        }

        public void ChangeState(Transition transition)
        {
            this.CurrentElement = transition;

            this.CurrentState = transition.To;

            if (transition.PushOnStack())
            {
                this.History.Push(this.CurrentState.Name);
            }
        }

        public void Complete()
        {
            this.CurrentState = null;
        }

        public bool IsCompleted
        {
            get
            {
                return this.CurrentState == null;
            }
        }

        public bool IsCurrentStateValid(RequestContext context, out RouteData routeData)
        {
            routeData = null;

            if (this.Webflow.CheckStatePolicy != null)
            {
                return this.Webflow.CheckStatePolicy.IsCurrentStateValid(this, context, out routeData);
            }

            return true;
        }
    }
}
